#[derive(Debug)]
enum UsState {
    V22,
    V23,
    Some(i32),
}

#[derive(Debug)]
enum IpAddrKind {
    V4,
    V6,
    Other(UsState),
}

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

// _x 仍会将值绑定到变量，而 _ 则完全不会绑定
impl IpAddrKind {
    fn route(ip_type: IpAddrKind) -> i32 {
        match ip_type {
            IpAddrKind::V4 => {
                print!("V4");
                4
            }
            IpAddrKind::V6 => 6,
            IpAddrKind::Other(ok) => match ok {
                UsState::V22 => 22,
                UsState::V23 => 23,
                UsState::Some(x) => x,
            },
        }
    }
    fn matchx(ip_type: i32) -> i32 {
        match ip_type {
            1 | 2 => 0, //或
            3 => 3 % 2,
            4..=10 => ip_type % 5, //范围
            _ => 10,
        }
    }
}

#[derive(Debug)]
struct IpAddr {
    kind: IpAddrKind,
    address: String,
}

#[cfg(test)]
mod matchx {
    use crate::basex::basic::enumx::{IpAddr, IpAddrKind, Point, UsState, plus_one};

    #[test]
    fn char() {
        let four = IpAddrKind::V4;
        let loopback = IpAddr {
            kind: IpAddrKind::V6,
            address: String::from("::1"),
        };
        println!("{:?}", IpAddrKind::route(four));
        println!("{:#?}", loopback);

        let four = IpAddrKind::Other(UsState::Some(12));
        println!("{:?}", IpAddrKind::route(four));
    }

    #[test]
    fn opt() {
        let five = Some(5);
        let six = plus_one(five);
        let none = plus_one(None);
        println!("{:?}", six);
        println!("{:#?}", none);
    }

    #[test]
    fn if_let() {
        let some_u8_value = IpAddrKind::Other(UsState::V22);
        // match some_u8_value {
        //     Some(3) => println!("three"),
        //     _ => (),
        // }

        if let IpAddrKind::V4 = some_u8_value {
            println!("V4");
        }
        if let IpAddrKind::Other(UsState::V23) = some_u8_value {
            println!("V23");
        }
        if let IpAddrKind::Other(UsState::V22) = some_u8_value {
            println!("V22");
        }
    }

    #[test]
    fn matches() {
        let arr = vec![UsState::V23, UsState::V22, UsState::V23, UsState::V23];
        //过滤
        let filter = arr.iter().filter(|x| match x {
            UsState::V23 => true,
            _ => false,
        });
        println!("{:?}", filter);
        let asd = arr.iter().filter(|x| matches!(x, UsState::V23));
        println!("{:?}", asd);
        let foo = 'f';
        assert!(matches!(foo, 'A'..='Z' | 'a'..='z'));
    }

    // 无论是 match 还是 if let，这里都是一个新的代码块，而且这里的绑定相当于新变量，如果你使用同名变量，会发生变量遮蔽:
    #[test]
    fn shadow() {
        // = 右边 Some(i32) 类型的 age 被左边 i32 类型的新 age 遮蔽了
        let age = Some(12);
        println!("在匹配前，age是{:?}", age);
        if let Some(age) = age {
            println!("匹配出来的age是{}", age);
        }
        println!("在匹配后，age是{:?}", age);

        if let Some(index) = age {
            println!("匹配出来的index是{}", index);
        }
        println!("在匹配后，age是{:?}", age);
    }

    // 只要模式匹配就一直进行 while 循环
    #[test]
    fn while_let() {
        // Vec是动态数组
        let mut stack = Vec::new();

        // 向数组尾部插入元素
        stack.push(1);
        stack.push(2);
        stack.push(3);

        // stack.pop从数组尾部弹出元素
        while let Some(top) = stack.pop() {
            println!("{}", top);
        }

        // let 语句 代表将匹配的值绑定到变量 x 上。因此，在 Rust 中,变量名也是一种模式
        // let PATTERN = 21;
    }

    fn match_struct(p: Point) {
        match p {
            Point { x, y: 0 } => println!("On the x axis at {}", x),
            Point { x: 0, y } => println!("On the y axis at {}", y),
            Point { x, y } => println!("On neither axis: ({}, {})", x, y),
        }
    }

    #[test]
    fn match_arr() {
        let arr: [u16; 2] = [114, 514];
        let [x, y] = arr; //结构数组 用 .. 忽略剩余值

        let arr: &[u16] = &[114, 514];
        if let [x, ..] = arr {
            assert_eq!(x, &114);
        }
    }

    //匹配守卫（match guard）是一个位于 match 分支模式之后的额外 if 条件，它能为分支模式提供更进一步的匹配条件
    #[test]
    fn match_guard() {
        let num = Some(4);
        let y = 10;
        match num {
            Some(x) if x < 5 => println!("less than five: {}", x),
            Some(x) if x < 10 => println!("{}", x),
            Some(n) if n == y => println!("Matched, n = {}", n),
            None => println!("None"),
            _ => (),
        }

        {
            println!("===========");
            let x = 7;
            let y = true;
            // 这个匹配条件表明此分支只匹配 x 值为 4、5 或 6 同时 y 为 true 的情况。
            // (4 | 5 | 6) if y => ...
            match x {
                4 | 5 | 6 if y => println!("yes"),
                _ => println!("no"),
            }
        }
    }

    #[test]
    fn match_ignore() {
        let mut setting_value = Some(5);
        let new_setting_value = Some(10);
        match (setting_value, new_setting_value) {
            (Some(_), Some(_)) => {
                println!("Can't overwrite an existing customized value");
            }
            _ => {
                setting_value = new_setting_value;
            }
        }

        println!("setting is {:?}", setting_value);
    }

    //     @（读作 at）运算符允许为一个字段绑定另外一个变量
    #[test]
    fn match_at() {
        // 绑定新变量 `p`，同时对 `Point` 进行解构
        let p @ Point { x: px, y: py } = Point { x: 10, y: 23 };
        println!("x: {}, y: {}", px, py);
        println!("{:?}", p);

        let point = Point { x: 10, y: 5 };
        if let p @ Point { x: 10, y } = point {
            println!("x is 10 and y is {} in {:?}", y, p);
        } else {
            println!("x was not 10 :(");
        }

        match 1 {
            // num @ 1 | 2 => { //err
            //     println!("{}", num);
            // }
            num @ (1 | 2) => {
                println!("{}", num);
            }
            _ => {}
        }
    }
}

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => {
            println!("{:?}", x);
            Some(i + 1)
        }
    }
}
